NAME LCD_350669_ASM
	#include <ioavr.h>						;

	//#define __TEST_TIMING__					// test timing enabled

	#define	b_CS_LCDn	3					; LCD CS: high - lcd interf. not accessed
	#define	b_RSel		1					; LCD register select
	#define	b_Lcd_RW	6					; High = Read ; Low = Write
	#define	b_Lcd_E		7					; RW=high: E_RD = high: data output

	#define	b_SDA		1					;
	#define	b_SCL		0					;
	#define test1_pin 	b_SDA
	#define test2_pin 	b_SCL

//	extern LCD_WIDTH						;
	extern lcd_RAM 							;
//	extern WriteInst 						;
	PUBLIC upd_LCD_RAM 					; export symbol to C function
	PUBLIC _upd_LCD_area_abs				;
	PUBLIC WriteCmd						;

	RSEG CODE:CODE:NOROOT(1)
	

/*******************************************************************************
//	void upd_LCD_RAM (void)
*******************************************************************************/
upd_LCD_RAM: 								; Function label

  /****************************************************************************/
  /* internal offset 14 lines:					COM 14-63 -> line  0..49	  */	
  /*											COM 84-113 -> line 70..99	  */	
  /* page 0..6 -> page 0..6, i_line 0..49 -> line 0..49, 50..55 unused COMS	  */			
  /* page 6 (<<4) -> page 8, i_line 50,51 -> line 70,71, 64..69 unused COMS	  */	
  /* page 6..9 -> page 9..12, i_line 52..79 -> line 72..99, 100-103 unused	  */	
  /* ( page n (high nibble >> 4 , page n+1 low nibble << 4 ) -> page n+3 )	  */
  /****************************************************************************/

#ifndef __I2C__								// ++TEST
  #ifdef __TEST_TIMING__
	cbi	PORTD, test1_pin ;					// testpin low
  #endif
#endif

	ldi		ZL, LOW(lcd_RAM)				; Init Z-Pointer
	ldi		ZH, (lcd_RAM) >> 8

	// Initial Display Line	0
	ldi		R16, 0x40						;
	rcall	WriteCmd						; Initial Display Line	
	ldi		R16, 0x00						;
	rcall	WriteCmd						; start at line 0	
	// Set Column Adress MSB (C6...C4), LSB (C3..C0)
    ldi		R16, 0x10						;
	rcall	WriteCmd						; Set Column Adress MSB
	ldi		R16, 0x00						;
	rcall	WriteCmd						; Set Column Adress LSB


	/* page 0..6 -> page 0..6, i_line 0..49 -> line 0..49, 50..55 unused COMS	  */			
	
	ldi		R16, 0xB0						; R16: Instruct. included page (0)

_upd_LCD_lp1:					; for (Byte page=0; page<7; page++) {
	rcall	WriteCmd			; <-		; Set Page Address (0..6), page in R16
	cbi 	PORTF, b_CS_LCDn 				; Enable LCD-I/O	
	sbi		PORTF, b_RSel 					; RS=1: Data		|_
	cbi		PORTC, b_Lcd_RW 				; RW=0: Set Write	|  address to Data Register

	ldi		R18, 0x80						; loop init: counter = 128
_upd_LCD_lp2:						; for (Byte i=startX; i<LCD_WIDTH ; i++) {
									; <-
	ld		R17, Z+							; load value from lcd_RAM, post-incr. pointer
	out 	PORTA, R17						; out DATAPORT, byData
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low (2 x Output, no greyscale)
	
	dec		R18								; decrement counter (i)
	brne	_upd_LCD_lp2		; ->	

	inc 	R16
	cpi		R16, 0xB7						; end loop when page = 7
	brne	_upd_LCD_lp1	; ->


	/* page 6 (<<4) -> page 8, i_line 50,51 -> line 70,71, 64..69 unused COMS	  */	

	movw	R23:R22, ZH:ZL					; R23:R22 <- Z (Pos 0, Page 7)
	ldi		R17, 0x80						; Z-Pointer -128
	add		ZL, R17
	brcs	_upd_LCD_1
	dec		ZH							
_upd_LCD_1:
	movw	R21:R20, ZH:ZL					; R21:R20 <- Z (Pos 0, Page 6)
	ldi		R16, 0xB8
	rcall	WriteCmd						; Set Page Address 8, page in R16
	cbi 	PORTF, b_CS_LCDn 				; Enable LCD-I/O	
	sbi		PORTF, b_RSel 					; RS=1: Data		|_
	cbi		PORTC, b_Lcd_RW 				; RW=0: Set Write	|  address to Data Register

	ldi		R18, 0x80						; loop init: counter = 128
_upd_LCD_lp3:						; for (Byte i=startX; i<LCD_WIDTH ; i++) {
									; <-
	ld		R17, Z+							; load value from lcd_RAM, post-incr pointer
	swap	R17								; instead of << 4
	out 	PORTA, R17						; out DATAPORT, byData
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low (2 x Output, no greyscale)
	
//	adiw	ZH:ZL, 0x01						; pointer to next value from lcd_RAM
	dec		R18								; decrement counter (i)
	brne	_upd_LCD_lp3		; ->	


	/* page 6..9 -> page 9..12, i_line 52..79 -> line 72..99, 100-103 unused	  */	
	
	ldi		R16, 0xB9						; R16: Instruct. included page 9

_upd_LCD_lp4:					; for (Byte page=6; page<10; page++) {
	rcall	WriteCmd			; <-		; Set Page Address (9..12), page in R16
	cbi 	PORTF, b_CS_LCDn 				; Enable LCD-I/O	
	sbi		PORTF, b_RSel 					; RS=1: Data		|_
	cbi		PORTC, b_Lcd_RW 				; RW=0: Set Write	|  address to Data Register

	ldi		R18, 0x80						; loop init: counter = 128
	
_upd_LCD_lp5:						; for (Byte i=startX; i<LCD_WIDTH ; i++) {
									; <-
	movw	ZH:ZL, R21:R20					; Z <- R21:R20 (Pos i, Page 6 .. )	
	ld		R17, Z+							; load value from lcd_RAM, post-incr. pointer
	movw	R21:R20, ZH:ZL 					; R21:R20 <- Z (Pos i+1, Page 6 .. )	
	andi	R17, 0xF0						; mask high nibble
	
	movw	ZH:ZL, R23:R22					; Z <- R23:R22 (Pos i, Page 7 .. )	
	ld		R19, Z+							; load lcd_RAM+1
	movw	R23:R22, ZH:ZL 					; R23:R22 <- Z (Pos i+1, Page 7 .. )	
	andi	R19, 0x0F

	or		R17,R19							; calculate coms
	swap	R17
	out 	PORTA, R17						; out DATAPORT, byData
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low (2 x Output, no greyscale)
	
	dec		R18								; decrement counter (i)
	brne	_upd_LCD_lp5		; ->	

	inc 	R16
	cpi		R16, 0xBD						; end loop when page = 7
	brne	_upd_LCD_lp4	; ->

	sbi		PORTF, b_CS_LCDn				; Disable LCD-I/O

#ifndef __I2C__								// ++TEST
  #ifdef __TEST_TIMING__
	sbi	PORTD, test1_pin ;					// testpin high
  #endif
#endif

	ret										; Return


_upd_LCD_area_abs: 								; Function label
/*******************************************************************************
/	void _upd_LCD_area_abs ( x1(R16), y1(R17), x2(R18), y2(R19) )
/
/		writes reserved RAM area to Display-RAM
/
/	Parameters:
/		x1 (R16)	- the horizontal coordinate upper left corner of area
/		y1 (R17)	- the vertical coordinate upper left corner
/		x2 (R18)	- the horizontal coordinate lower right corner of area
/		y2 (R19)	- the vertical coordinate lower right corner
/
/	Register usage:
/		R18 (startPage)	- calculated page from y-coordinates
/		R19 (endPage)	- calculated page from y-coordinates
/		R20 (startX)	- left column adress
/		R21 (endX)		- right column adress
/		R22 			- column counter for currently adressed value
/		R16, R17		- temporarely used for subroutines calls
/
*******************************************************************************/

#ifndef __I2C__								// ++TEST
  #ifdef __TEST_TIMING__
	cbi	PORTD, test1_pin ;					// testpin low
  #endif
#endif

	mov		R20, R16						; R16 -> R20 (startX)
	mov		R21, R18 						; R18 -> R21 (endX)

/* respecting leck in coms lines numbering ( com' 50 .. com' 69 )			  */
	//  if ( y1 <= 49 ) startPage = y1/8 ; else startPage = (y1-4)/8 + 3 ;
	//  if ( y2 <= 49 ) endPage = y2/8 ; else endPage = (y2-4)/8 + 3 ;
	cpi		R17, 50							;
	brpl	_updLCDaabs1					; branch if R17 >= 50
	lsr		R17								; y1 / 2 -> R17
	lsr		R17								; y1 / 4 -> R17
	lsr		R17								; y1 / 8 -> R17
	rjmp	_updLCDaabs2	
_updLCDaabs1:
	subi	R17, 4							; y1 - 4 -> R17
	lsr		R17								; ( y1 - 4 ) / 2 -> R17
	lsr		R17								; ( y1 - 4 ) / 4 -> R17
	lsr		R17								; ( y1 - 4 ) / 8 -> R17
	subi	R17, 253						; ( y1 - 4 ) / 8 + 3 -> R17
_updLCDaabs2:
	mov		R18, R17						; ( y1 - 4 ) / 8 + 3 -> R18 (startPage)

	cpi		R19, 50							; end loop when page = 7
	brpl	_updLCDaabs3					; branch if R19 >= 50
	lsr		R19								; y1 / 2 -> R19
	lsr		R19								; y1 / 4 -> R19
	lsr		R19								; y1 / 8 -> R19
	rjmp	_updLCDaabs4	
_updLCDaabs3:
	subi	R19, 4							; y1 - 4 -> R19
	lsr		R19								; ( y1 - 4 ) / 2 -> R19
	lsr		R19								; ( y1 - 4 ) / 4 -> R19
	lsr		R19								; ( y1 - 4 ) / 8 -> R19
	subi	R19, 253						; ( y1 - 4 ) / 8 + 3 -> R19 (endPage)
_updLCDaabs4:

/* enable lcds DMA mode, define DMA area(startX; startPage )-(endX; endPage)  */
	ldi		R16, 0xF4						; Instruct. LCD DMA mode ..
	rcall	WriteCmd						;
	ldi		R16, 0x05						; .. switched ON
	rcall	WriteCmd						;
	ldi		R16, 0x84						; Instruct. set start/end column ..
	rcall	WriteCmd						; .. and page adress
	mov		R16, R20						; start column, ..
	rcall	WriteCmd						;
	mov		R16, R18						; start page, ..
	rcall	WriteCmd						;
	mov		R16, R21						; end column, ..
	rcall	WriteCmd						;
	mov		R16, R19						; end page, ..
	rcall	WriteCmd						;
	
	cpi		R18, 7							;
	brpl	_updLCDaabs60					; branch if startPage >= 7

/*	if ( startPage <=6 ) {													  */	
	ldi		R16, 0xB0
	add		R16, R18						; ( 0xB0 + startPage ) -> R16
	rcall	WriteCmd						; Set Page Address (startPage)
	mov		R16, R20						; startX -> R16
	swap	R16								; X3 X2 X1 X0 X7 X6 X5 X4
	andi	R16, 0x07						; Set Column Adress MSB ..
	ori		R16, 0x10
	rcall	WriteCmd						; .. (0 0 0 1  0 X6 X5 X4 )
	mov		R16, R20						; startX -> R16
	andi	R16, 0x0F						; Set Column Adress LSB ..
	rcall	WriteCmd						;  .. (0 0 0 0  X3 X2 X1 X0 )

	cbi 	PORTF, b_CS_LCDn 				; Enable LCD-I/O	
	sbi		PORTF, b_RSel 					; RS=1: Data		|_
	cbi		PORTC, b_Lcd_RW 				; RW=0: Set Write	|  address to Data Register


_updLCDaabs6Lp2:				; <-
	mov		R22, R20						; startX -> R22 (columnCnt)
	rcall	_calcLCD_RAMptr					; calculate RAM adress (startX, currentPage)

_updLCDaabs6Lp1:					; <-
	ld		R16, Z+							; load value from lcd_RAM, post-incr. pointer
	out 	PORTA, R16						; out DATAPORT, byData
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low (2 x Output, no greyscale)
	
	inc		R22								; increment column counter (i)
	cp		R21, R22		
	brsh	_updLCDaabs6Lp1			; ->	

	inc 	R18								; next page
	cpi		R19, 6
	brlo	_updLCDaabs7					; endPage < 6 ?
	cpi		R18, 7
	brlo	_updLCDaabs6Lp2		; ->
	rjmp	_updLCDaabs8
_updLCDaabs7:
	cp		R19, R18
	brsh	_updLCDaabs6Lp2		; ->

_updLCDaabs8:
	cpi		R19, 8	
	brpl	_updLCDaabs9					; branch if endPage >= 8
	rjmp	_updLCDaabs80					; Return, job done !!

_updLCDaabs60:
	subi	R18, 03							; calculate (startPage)
	rjmp	_updLCDaabs61

/*	if ( endPage >=8 ) {													  */	
_updLCDaabs9:
	ldi		R18, 05							; repeat rest of Page 5 (startPage) -> LCD page 8
_updLCDaabs61:
	subi	R19, 03							; R19-3 -> R19, recalculate loop-end-condition
	ldi		R16, 0xB3
	add		R16, R18						; ( 0xB3 + startPage ) -> R16
	rcall	WriteCmd						; Set Page Address (startPage)
	mov		R16, R20						; startX -> R16
	swap	R16								; X3 X2 X1 X0 X7 X6 X5 X4
	andi	R16, 0x07						; Set Column Adress MSB ..
	ori		R16, 0x10
	rcall	WriteCmd						; .. (0 0 0 1  0 X6 X5 X4 )
	mov		R16, R20						; startX -> R16
	andi	R16, 0x0F						; Set Column Adress LSB ..
	rcall	WriteCmd						;  .. (0 0 0 0  X3 X2 X1 X0 )

	cbi 	PORTF, b_CS_LCDn 				; Enable LCD-I/O	
	sbi		PORTF, b_RSel 					; RS=1: Data		|_
	cbi		PORTC, b_Lcd_RW 				; RW=0: Set Write	|  address to Data Register

_updLCDaabs9Lp2:				; <-
	mov		R22, R20						; startX -> R22 (columnCnt)
	rcall	_calcLCD_RAMptr					; calculate RAM adress (startX, currentPage)

_updLCDaabs9Lp1:					; <-
	ld		R17, Z+							; load value from lcd_RAM, post-incr. pointer
	andi	R17, 0xF0						; mask high nibble
	push	ZL								; save Z-pointer to stack
	push	ZH
	ldi		R16, 127						; temporary term
	add		ZL, R16							; calculate pointer to next page location
	brcc	_updLCDaabs10
	inc		ZH			
_updLCDaabs10:
	ld		R16, Z							; load value from lcd_RAM
	pop		ZH								; restore Z-pointer from stack
	pop		ZL
	andi	R16, 0x0F						; mask low nibble
	or		R16,R17							; calculate coms
	swap	R16
	out 	PORTA, R16						; out DATAPORT, byData
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low
	sbi		PORTC, b_Lcd_E 					; E high
	cbi		PORTC, b_Lcd_E 					; E low (2 x Output, no greyscale)

	inc		R22								; increment column counter (i)
	cp		R21, R22		
	brsh	_updLCDaabs9Lp1			; ->	

	inc 	R18								; next page
	cp		R19, R18
	brsh	_updLCDaabs9Lp2		; ->

_updLCDaabs80:
/* disable lcds DMA mode, default DMA area(startX; startPage )-(endX; endPage)  */
	ldi		R16, 0x84						; Instruct. set start/end column ..
	rcall	WriteCmd						; .. and page adress
	ldi		R16, 0							; start column, ..
	rcall	WriteCmd						;
	ldi		R16, 0							; start page, ..
	rcall	WriteCmd						;
	ldi		R16, 127						; end column, ..
	rcall	WriteCmd						;
	ldi		R16, 15							; end page, ..
	rcall	WriteCmd						;

	ldi		R16, 0xF4						; Instruct. LCD DMA mode ..
	rcall	WriteCmd						;
	ldi		R16, 0x01						; .. switched OFF
	rcall	WriteCmd						;

#ifndef __I2C__								// ++TEST
  #ifdef __TEST_TIMING__
	sbi	PORTD, test1_pin ;					// testpin high
  #endif
#endif

	ret										; Return


/*******************************************************************************
/	word _calcLCD_RAMptr(R20)
/		Parameter:			R20					; column start adress
/							R18					; startPage	
/		Result:				ZH:ZL				; Z-Pointer
/		Use of register:	R16, R17				
*******************************************************************************/
_calcLCD_RAMptr:									
	ldi		ZL, LOW(lcd_RAM)				; Init Z-Pointer
	ldi		ZH, (lcd_RAM) >> 8
	add		ZL, R20							; add offset (startX)
	brcc	_calcLCD_RAMptr1					
	inc		ZH								; addition carry
_calcLCD_RAMptr1:
	clc										; C:=0
	mov		R16, R18						; startpage -> R16 (counter)
	ror		R16								; R1 -> R0 -> C -> R7 -> ..
	mov		R17, R16
	ror		R16								; R1 -> R0 -> C -> R7 -> .. (startPage *128)
	andi	R16, 0x80						; maskout different factors from 128
	add		ZL, R16							; (R16 mod 2) * 128
	brcc	_calcLCD_RAMptr2
	inc		ZH							
_calcLCD_RAMptr2:
	add		ZH, R17							; (R16/2) * 256
	ret



/*******************************************************************************
//	_WriteCmd(R16)	; uses also R17
*******************************************************************************/
WriteCmd:									
	CBI     PORTF, b_CS_LCDn				; Enable LCD-I/O
	LDI     R17, 255						; load temporary register
	OUT     DDRA, R17						; Port A output / data out
	CBI     PORTF, b_RSel					; Control cmd	\___
	CBI     PORTC, b_Lcd_RW					; Set Write  	/   address to Instruction Register
	OUT     PORTA, R16						; outp. instruction on Port A
	SBI     PORTC, b_Lcd_E					; E high (data strobe)
	CBI     PORTC, b_Lcd_E					; E low  (data strobe)
	SBI     PORTF, b_CS_LCDn				; Disable LCD-I/O
	RET

END

/************************  E n d   o f   F i l e  ****************************/


